use std::sync::{Arc, Mutex};
use std::{usize, vec};

use crate::core::error::operation_error::OperationError;
use crate::core::operation::Operation;
use grpc_server::State::{Running, Stopped};
use grpc_server::Step as PbStep;
use grpc_server::{OkType, State};
use crate::core::step_context::StepContext;
use chrono::{DateTime, Utc};
use crate::core::services::job::job_context::JobContext;

pub struct Step {
    id: usize,
    state: Mutex<State>,
    result: Mutex<Option<Result<OkType, OperationError>>>,
    message: Mutex<String>,
    create_time: DateTime<Utc>,
    start_time: Mutex<DateTime<Utc>>,
    state_time: Mutex<DateTime<Utc>>,

    operation: tokio::sync::Mutex<Box<dyn Operation>>,
    depends_on: Vec<usize>,
}

impl Step {
    pub fn id(&self) -> usize {
        self.id
    }
    pub fn state(&self) -> State {
        *self.state.lock().unwrap()
    }
    pub fn result(&self) -> Option<Result<OkType, String>> {
        (*self.result.lock().unwrap()).as_ref().map(|r| match r {
            Ok(ok_type) => Ok(*ok_type),
            Err(e) => Err(e.to_string()),
        })
    }
    pub fn message(&self) -> String {
        (*self.message.lock().unwrap()).to_string()
    }
    pub fn create_time(&self) -> DateTime<Utc> {
        self.create_time
    }
    pub fn start_time(&self) -> DateTime<Utc> {
        *self.start_time.lock().unwrap()
    }

    pub fn state_time(&self) -> DateTime<Utc> {
        *self.state_time.lock().unwrap()
    }

    pub fn pb_step(&self) -> PbStep {
        PbStep {
            id: self.id.try_into().unwrap(),
            state: (*self.state.lock().unwrap()).into(),
            create_timestamp: self.create_time.timestamp(),
            start_timestamp: self.start_time.lock().unwrap().timestamp(),
            state_timestamp: self.state_time.lock().unwrap().timestamp(),
            estimated_time: Default::default(),
            message: "".to_string(),
            depends_on: vec![],
        }
    }

    pub fn new(id: usize, operation: Box<dyn Operation>) -> Self {
        Step {
            id,
            state: Default::default(),
            result: Mutex::new(None),
            message: Mutex::new(String::new()),
            create_time: Utc::now(),
            start_time: Default::default(),
            state_time: Default::default(),
            operation: tokio::sync::Mutex::new(operation),
            depends_on: vec![],
        }
    }

    pub async fn run(&self, context: Arc<JobContext>) -> bool {
        let mut opt = self.operation.lock().await;
        *self.start_time.lock().unwrap() = Utc::now();
        self.change_state(Running);
        context.send_change();
        let context_cloned = context.clone();
        let result = opt
            .run(Arc::new(StepContext::new(context_cloned)))
            .await
            .map(|_| OkType::Completed);
        self.change_state(Stopped);
        context.send_change();

        let is_ok = result.is_ok();
        *self.result.lock().unwrap() = Some(result);

        is_ok
    }

    fn change_state(&self, s: State) {
        *self.state.lock().unwrap() = s;
        *self.state_time.lock().unwrap() = Utc::now();
    }
}
